/** 
Copyright (c) 2010, Sheikh Abdul Wahid Ahmed
Details @ http://extremecodeworld.codeplex.com/license
**/

using System;
using CodeGenerator.Definitions;
using System.Text;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Collections;
using EXtremecode.Utils;
using EXtremecode.Common; 

namespace CodeGenerator.Managers
{
	/// <summary>
	/// Summary description for CustomCodeExchangeManager.
	/// </summary>
	public class CustomCodeExchangeManager
	{
		private static CustomCodeExchangeManager singleton=null;
		
		/// <summary>
		/// Instance
		/// </summary>
		public static CustomCodeExchangeManager Instance
		{
			get
			{
				if(singleton==null)
				{
					singleton=new CustomCodeExchangeManager();
					//throw Logging.Logger.Write(new Exception("Singleton object not created till yet"));
				}
				return singleton;
			}
		}

		
		/// <summary>
		/// CreateInstance
		/// </summary>
		/// <param name="sessions"></param>
		/// <returns></returns>
		public static CustomCodeExchangeManager CreateInstance()
		{
			
			if(singleton==null)
			{
				singleton=new CustomCodeExchangeManager();
			}
			return singleton;
		}
		private CustomCodeExchangeManager(){}

		
		/// <summary>
		/// ExchaneCustomCode
		/// </summary>
		/// <param name="generatedCode">code which currently generated by generator.</param>
		/// <param name="currentCode">code which was generated before and modified by user
		///  [only in custom code region]
		/// </param>
		public void ExchangeCustomCode(StringBuilder generatedCode,StringBuilder currentCode)
		{
			Regex regex=new Regex(@"((\/\*\*)|(<!--)|(<%--))(\t|\s)*Custom Region Start(|\t|\s)*\[(?<CustomCodeKey>.+?)\](\t|\s)*((\*\*\/)|(-->)|--%>)(?<CustomCode>(.|\t|\r|\n|\s)*?)((\/\*\*)|(<!--)|(<%--))(|\t|\s)*Custom Region End(|\t|\s)*(\t|\s)*((\*\*\/)|(-->)|(--%>))"
				,RegexOptions.Multiline);
			
			//Debugging.Debug.VerifyExpression(@"((\/\*\*)|(<!--)|(<%--))(\t|\s)*Custom Region Start(|\t|\s)*\[(?<CustomCodeKey>.+?)\](\t|\s)*((\*\*\/)|(-->)|--%>)(?<CustomCode>(.|\t|\r|\n|\s)*?)((\/\*\*)|(<!--)|(<%--))(|\t|\s)*Custom Region End(|\t|\s)*(\t|\s)*((\*\*\/)|(-->)|(--%>))",generatedCode.ToString());
			MatchCollection generatedCodeMatchCol=regex.Matches(generatedCode.ToString());
			MatchCollection currentCodeMatchCol=regex.Matches(currentCode.ToString());
			//Debugging.Debug.VerifyExpression(regex.ToString(),generatedCode);
			//Debugging.Debug.VerifyExpression(regex.ToString(),currentCode);

			
			//Make hashtables for one to one mapping.
			Hashtable htGeneratedCode=new Hashtable();
			Hashtable htCurrentCode=new Hashtable();
			
			
			
			
			//For Generated Codes

			//int customCodeSizeDifference=0;//used to store the diffrence between size of the 
			//custom code. if we insert custom code of current file into generted one
			//starting position of next custom code will be changed with the 
			//value of 'customCodeSizeDifference'.In other word regex matchposition is for 
			//before the insertion/deletion of content of custom code and now we cant rely only on
			//this value. we have to add difference which is stored on this variable.
			//BUT now we can not resolve this problem by using single variable. We have to use sorted array of indexes
			//of starting position of generated code.
			//While replacement the indexes which comes after modified index have to updated but above indexes should
			//not be updated.
			SortedList customCodeSizeDifference =  new SortedList();

			for(int i=0; i<generatedCodeMatchCol.Count; i++)
			{
				Match match=generatedCodeMatchCol[i]; 
                string hashKey = match.Groups["CustomCodeKey"].Value;
				if(hashKey.Trim()==string.Empty)
				{
					//if keycode left empty, it may create problem so simply generate error
					throw new Exception("Custom code key should be defined [Generated Code]");
				}
				
				//check for unique key.
				if(htGeneratedCode.Contains(hashKey.Trim()))
				{
					throw new Exception(
							string.Format("key '{0}' defined more than one time. Custom code key should be unique atleast" +
								" in page level [Generated Code]",hashKey));
				}
				else
				{
					htGeneratedCode.Add(hashKey,match);
					customCodeSizeDifference.Add(match.Groups["CustomCode"].Index,
						match.Groups["CustomCode"].Index); 
				}
			}

			//For Current Codes
			for(int i=0; i<currentCodeMatchCol.Count; i++)
			{
				Match match=currentCodeMatchCol[i]; 
				string hashKey = match.Groups["CustomCodeKey"].Value;
				string customCode = match.Groups["CustomCode"].Value;

				if(hashKey.Trim()==string.Empty)
				{
					//if keycode left empty, it may create problem so simply generate error
					throw new Exception("Custom code key should be defined [Current Code]");
				}
				
				//check for unique key.
				if(htCurrentCode.Contains(hashKey.Trim()))
				{
					throw new Exception(
						string.Format("key '{0}' defined more than one time. Custom code key should be unique atleast" +
						" in page level [Current Code]",hashKey));
				}
				else if(customCode.Trim('\n','\r','\t')!= string.Empty)
				{
					//if current custom is empty then no need to change generated
					//custom code with the current one and no need to add in hash table.
					//in short we will have to use generated custom code.
					
					htCurrentCode.Add(hashKey,match);
				}
			}
			
			
			
			//Now Check  Custom Code Key Conflicts.
			string  conflictKeys=string.Empty;
			string quma=string.Empty;
			foreach(string hashKey in htCurrentCode.Keys)
			{
				
				if(!htGeneratedCode.Contains(hashKey))
				{
					conflictKeys += quma + hashKey; 
					quma=" , ";
				}
			}
			if(conflictKeys != String.Empty)
			{
				
				//if the keys defined in current code and not defined in generated code  then 
				//there is chance of lost of custom code thats why in such
				//case we need to know, what does the user want.
					
				if(CommonDelegates.OnWarning != null)
				{
					bool cont = CommonDelegates.OnWarning(
						string.Format("the key(s) '{0}' are not defined in generated code, the code written" +
						" in the listed keys of regions will be lost.\nDo want to continue.",conflictKeys));
					if(!cont)
					{
						return;
					}
				}
				else
				{
					throw new Exception("conflict in custom code.\nFor avoiding exception, attach a handler" +
						"with the delegate 'OnWarning'");
				}
			}
			


			//Now we can apply one to one mapping.
			foreach(string hashKey in htCurrentCode.Keys)
			{
				if(!htGeneratedCode.ContainsKey(hashKey))
				{
					//--conflict
					//it is supposed that it has been ok by user.
					continue;
				}

				
				Match currentCodeMatch = htCurrentCode[hashKey] as Match;
				Match generatedCodeMatch = htGeneratedCode[hashKey] as Match;
				
				Capture currentCustomCodeCapture=
					currentCodeMatch.Groups["CustomCode"]; 
				Capture generatedCustomCodeCapture=
					generatedCodeMatch.Groups["CustomCode"]; 
					
				//Debugging.Debug.ShowText(generatedCode);
				generatedCode.Remove((int)customCodeSizeDifference[generatedCustomCodeCapture.Index],generatedCustomCodeCapture.Value.Length);  
				generatedCode.Insert((int)customCodeSizeDifference[generatedCustomCodeCapture.Index],currentCustomCodeCapture.Value);
					
				//update indexes
				int start= customCodeSizeDifference.IndexOfKey(generatedCustomCodeCapture.Index);
				int differenceOfIndexes=currentCustomCodeCapture.Value.Length-generatedCustomCodeCapture.Value.Length;

				for(int i=start+1; i<customCodeSizeDifference.Count; i++)
				{
					int customCodeindex=(int)customCodeSizeDifference.GetByIndex(i);
					customCodeindex += differenceOfIndexes;
					customCodeSizeDifference.SetByIndex(i,customCodeindex);
				}
						
			}

			
		}
	}



}
